﻿using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Channels;
using System.Threading;
using GeneralDFS.BusinessLogical.RemoteServers;
using GeneralDFS.Common.Network;
using GeneralDFS.DataEntity.Enum;
using GeneralDFS.Interface.Nodes;
using GeneralDFS.DataEntity.IO;
using GeneralDFS.DataEntity.Config;
using GeneralDFS.DataEntity.Nodes;
using GeneralDFS.BusinessLogical.FileSystemLogical;
using GeneralDFS.BusinessLogical.RemoteObject;
using GeneralDFS.BusinessLogical.IO;
using NIF.MobileActors.Core;
using NIF.MobileActors.Hosts;
using Timer = System.Timers.Timer;

namespace GeneralDFS.BusinessLogical.Nodes
{
    [Serializable]
    public partial class NameNode:IBaseNode
    {
        #region Private Values

        private NameNodeRemoteServer _rpcServer;
        private NameNodeConfig _nameNodeConfig;
        private FsImage _fsImage;
        private NameNodeRemoteObject _nameNodeRemoteObject;
        private Timer _updateHeartBeatTimer;
        private IChannel _nameNodeChannel;//连接到Master的NameNode的Channel
        private List<NameNodeInfo> _nameNodeInfos; //同步主节点NameNode列表

        #endregion

        public DirectoryNodeInfo Root; //根目录
        public DFSConfig Config;//当前节点内的全局配置文件
        public NameNodeInfo LocalNode; //Contains namenode basic info 
        public NameNodeRemoteObject MasterNameNodeRemoteObject;
        public NameNodeInfo MasterNameNodeInfo;
        public BlockManager BlockManager;
        public DataNodeCenter DataNodeCenter;
        public FileSystem FileSystem;

        public void Start()
        {
            LogUtil.LogInfo("NameNode init at ip:{0} rpcPort:{1} serverName:{2}", _nameNodeConfig.Ip, _nameNodeConfig.RpcPort, _nameNodeConfig.ServerName);
            
            //1、实例化对象
            _fsImage=new FsImage(this);
            DataNodeCenter=new DataNodeCenter(this);
            BlockManager = new BlockManager(this,DataNodeCenter);
            Root = _fsImage.GetFileSystem();
            FileSystem = new FileSystem(this);
            _nameNodeRemoteObject=new NameNodeRemoteObject(this);
            LocalNode = new NameNodeInfo { Ip = _nameNodeConfig.Ip, RpcPort = _nameNodeConfig.RpcPort, ServerName = _nameNodeConfig.ServerName,AgentRpcPort = _nameNodeConfig.AgentRpcPort};
            LocalNode.SetState(NameNodeState.UnderContract); //Set the nodestate to Contract

            //2、启动Rpc服务
            _rpcServer=new NameNodeRemoteServer();
            _rpcServer.InitServer(_nameNodeRemoteObject);
            BlockManager.Start();
            DataNodeCenter.Start();
            //启动Agent服务
            RemotingHost.RegisterLocal(_nameNodeConfig.Ip,_nameNodeConfig.AgentRpcPort);
            RemotingHost.Node = this;

            //3、初始化chord组件
            var isFirst = true;
            foreach (var nameNodeInfo in Config.NameNodeInfos())
            {
                if(nameNodeInfo.ID==LocalNode.ID)
                    continue;
                var node = GetInstance(nameNodeInfo);
                if (IsInstanceValid(node))
                {
                    isFirst = false;
                    _nameNodeRemoteObject.Join(nameNodeInfo);
                    break;
                }
                //添加到配置中
                Thread.Sleep(1000);
            }
            if (isFirst)
            {
                var node = GetInstance(LocalNode);
                node.Join(null);
            }

            LocalNode.SetState(NameNodeState.Online);
            StartCheckHeartBeat();
        }

        public int Stop()
        {
            return Stop(0);
        }

        public int Stop(int enforce)
        {
            LocalNode.SetState(NameNodeState.Offline);
            _rpcServer.StopServer();
            DataNodeCenter.Stop();
            BlockManager.Stop();
            StopCheckHeartBeat();
            return 0;
        }

        public static NameNodeRemoteObject GetInstance(NameNodeInfo node)
        {
            if (node == null)
            {
                LogUtil.LogDebug("Invalid Node ({0}).", "Null Argument.");
                return null;
            }

            try
            {
                var nameNodeUri = String.Format("tcp://{0}:{1}/{2}-Server", node.Ip, node.RpcPort,
                                            node.ServerName);
                var retInstance = (NameNodeRemoteObject)Activator.GetObject(typeof(NameNodeRemoteObject), nameNodeUri);
                return retInstance;
            }
            catch (Exception e)
            {
                // perhaps instead we should just pass on the error?
                LogUtil.LogDebug("Unable to activate remote server {0}:{1} ({2}).", node.Ip, node.RpcPort, e.Message);
                return null;
            }
        }

        public static NameNodeInfo CallFindSuccessor(NameNodeInfo remoteNode, UInt64 id, int retryCount, int hopCountIn, out int hopCountOut)
        {
            var instance = NameNode.GetInstance(remoteNode);

            try
            {
                return instance.FindSuccessor(id, hopCountIn, out hopCountOut);
            }
            catch (System.Exception ex)
            {
                if (retryCount > 0)
                {
                    return CallFindSuccessor(remoteNode, id, --retryCount, hopCountIn, out hopCountOut);
                }
                else
                {
                    hopCountOut = hopCountIn;
                    return null;
                }
            }
        }

        public static NameNodeInfo[] CallLocalSuccessor(NameNodeInfo remoteNode)
        {
            var instance = GetInstance(remoteNode);
            if (IsInstanceValid(instance))
            {
                return new NameNodeInfo[]{remoteNode,instance.Successor};
            }
            return null;
        }

        /// <summary>
        /// Gets the remote Predecessor property, using a default retry value of three.
        /// </summary>
        /// <param name="remoteNode">The remote from which to access the property.</param>
        /// <returns>The remote node's predecessor, or NULL in case of error.</returns>
        public static NameNodeInfo GetPredecessor(NameNodeInfo remoteNode)
        {
            return GetPredecessor(remoteNode, 3);
        }

        /// <summary>
        /// Gets the remote Predecessor property, given a custom retry count.
        /// </summary>
        /// <param name="remoteNode">The remote node from which to access the property.</param>
        /// <param name="retryCount">The number of times to retry the operation in case of error.</param>
        /// <returns>The remote predecessor, or NULL in case of error.</returns>
        public static NameNodeInfo GetPredecessor(NameNodeInfo remoteNode, int retryCount)
        {
            var instance = GetInstance(remoteNode);

            try
            {
                return instance.Predecessor;
            }
            catch (System.Exception ex)
            {
                if (retryCount > 0)
                {
                    return GetPredecessor(remoteNode, --retryCount);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// 远程调用Notify方法
        /// </summary>
        /// <param name="remoteNode">远程节点</param>
        /// <param name="callingNode">待处理的节点</param>
        /// <returns></returns>
        public static bool CallNotify(NameNodeInfo remoteNode, NameNodeInfo callingNode)
        {
            return CallNotify(remoteNode, callingNode, 3);
        }

        /// <summary>
        /// 远程调用Notify方法
        /// </summary>
        /// <param name="remoteNode">远程节点</param>
        /// <param name="callingNode">待处理的节点</param>
        /// <param name="retryCount">重试的尝试次数</param>
        /// <returns></returns>
        public static bool CallNotify(NameNodeInfo remoteNode, NameNodeInfo callingNode, int retryCount)
        {
            var instance = GetInstance(remoteNode);

            try
            {
                instance.Notify(callingNode);
                return true;
            }
            catch (System.Exception ex)
            {
                if (retryCount > 0)
                {
                    return CallNotify(remoteNode, callingNode, --retryCount);
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Gets the remote SuccessorCache property, using a default retry value of three.
        /// </summary>
        /// <param name="remoteNode">The remote from which to access the Successor Cache.</param>
        /// <returns>The remote node's successorCache, or NULL in case of error.</returns>
        public static NameNodeInfo[] GetSuccessorCache(NameNodeInfo remoteNode)
        {
            return GetSuccessorCache(remoteNode, 3);
        }

        /// <summary>
        /// Gets the remote SuccessorCache property, given a custom retry count.
        /// </summary>
        /// <param name="remoteNode">The remote node from which to access the property.</param>
        /// <param name="retryCount">The number of times to retry the operation in case of error.</param>
        /// <returns>The remote successorCache, or NULL in case of error.</returns>
        public static NameNodeInfo[] GetSuccessorCache(NameNodeInfo remoteNode, int retryCount)
        {
            var instance = GetInstance(remoteNode);

            try
            {
                return instance.SuccessorCache;
            }
            catch (System.Exception ex)
            {
                if (retryCount > 0)
                {
                    return GetSuccessorCache(remoteNode, --retryCount);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Gets the remote Successor property, using a default retry value of three.
        /// </summary>
        /// <param name="remoteNode">The remote from which to access the property.</param>
        /// <returns>The remote node's successor, or NULL in case of error.</returns>
        public static NameNodeInfo GetSuccessor(NameNodeInfo remoteNode)
        {
            return GetSuccessor(remoteNode, 3);
        }

        /// <summary>
        /// Gets the remote Successor property, given a custom retry count.
        /// </summary>
        /// <param name="remoteNode">The remote node from which to access the property.</param>
        /// <param name="retryCount">The number of times to retry the operation in case of error.</param>
        /// <returns>The remote successor, or NULL in case of error.</returns>
        public static NameNodeInfo GetSuccessor(NameNodeInfo remoteNode, int retryCount)
        {
            var instance = GetInstance(remoteNode);

            try
            {
                return instance.Successor;
            }
            catch (System.Exception ex)
            {
                if (retryCount > 0)
                {
                    return GetSuccessor(remoteNode, --retryCount);
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// 判断远程Object是否有效
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        public static bool IsInstanceValid(NameNodeRemoteObject instance)
        {
            try
            {
                if (instance.Port > 0 && instance.Successor != null)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception e)
            {
                return false;
            }
        }

        public void ConfigNode(object config)
        {
            _nameNodeConfig = config as NameNodeConfig;
        }

        /// <summary>
        /// 配置Local模式下的当前节点
        /// </summary>
        /// <param name="settings"></param>
        public void ConfigLocal(object settings)
        {
            var currentSettings=settings as CurrentSettings;
            if (currentSettings != null)
                Config.CurrentSettings = new CurrentSettings
                    {
                        Index = currentSettings.Index,
                        NodeType = currentSettings.NodeType
                    };
        }

        public NameNodeConfig GetNodeConfig()
        {
            return _nameNodeConfig;
        }
    }
}